/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_OCTREESCENEMANAGER_H
#define M_OCTREESCENEMANAGER_H

#include "scenemanager.h"
#include "octree.h"
#include "octreerenderable.h"
#include "../mutex.h"

namespace Engine
{
	/**
	*   Specialized SceneManager that divides the geometry into an octree in order to facilitate spatial queries.
	*/
	class OctreeSceneManager : public SceneManager
	{
	public:
		typedef std::list <OctreeNode* > NodeList;
		typedef std::vector <std::pair<MeshPtr,Matrix4<REAL> > > MeshVector;

		/**
		*   Standard Constructor.  Initializes the octree to -10000,-10000,-10000 to 10000,10000,10000 with a depth of 8.
		*/
		OctreeSceneManager(const std::string& name);

		/**
		*   Destructor.
		*/
		~OctreeSceneManager();

		/**
		*   Initializes the manager to the given box and depth.
		*/
		void Init( const AABB& box, int d );

		/**
		*   Searches for visible objects and populate render queue.
		*/
		virtual void FindVisibleObjects(const Camera* camera);

		/**
		*   Add mesh to render as static batched geometry.
		*/
		virtual void AddStaticGeometry(const std::string& fname, const Matrix4<REAL>& transform);

		/**
		*   Create minimal renderables for added static geometry.
		*/
		virtual void BuildStaticGeometry();

		/**
		*   Walks through the octree, adding any visible objects to the render queue.
		*/
		void WalkOctree( const Camera* camera, RenderQueue* queue, RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList, Octree* octant,bool foundvisible);

		/**
		*   Checks the given OctreeNode, and determines if it needs to be moved to a different octant.
		*/
		void UpdateOctreeNode( OctreeNode* node);

		/**
		*   Removes the given octree node
		*/
		void RemoveOctreeNode(OctreeNode* );

		/**
		*   Adds the Octree Node, starting at the given octree, and recursing at max to the specified depth.
		*/
		void AddOctreeNode( OctreeNode*, Octree*, int depth = 0 );

		/**
		*   Delete static geometry and scene culling data.
		*/
		virtual void ResetScene();

	private:

		void FreeInputStaticGeom();

		void CopyVertexData(void* dest,unsigned int destOffset,const VertexElement* destElem,const void* src,unsigned int srcOffset,const VertexElement* srcElem, const Matrix4<REAL>& transform);

		void AddTriangle( const Triangle& tri, Octree* octant, int depth = 0 );

		class WalkTask: public BaseTask
		{
		public:
			WalkTask(OctreeSceneManager* manager, const Camera* camera, RenderQueue* queue, RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList, Octree* octant,bool foundvisible)
				:m_manager(manager)
				,m_camera(camera)
				,m_queue(queue)
				,m_shadowsQueue(shadowsQueue)
				,m_lightList(lightList)
				,m_octant(octant)
				,m_foundvisible(foundvisible)

			{}
			void Run()
			{
				m_manager->WalkOctree(m_camera, m_queue, m_shadowsQueue, m_lightList, m_octant, m_foundvisible);
			}

			OctreeSceneManager* m_manager;
			const Camera* m_camera;
			RenderQueue* m_queue;
			RenderQueue* m_shadowsQueue;
			std::vector<LightPtr> m_lightList;
			Octree* m_octant;
			bool m_foundvisible;
		};

	private:

		RefPtr<Octree> m_octree;///< /// The root octree

		int m_maxDepth; ///< /// Max depth for the tree

		AABB m_box;	///< Size of the octree

		MeshVector m_inputStaticGeom;
		RefPtr<VertexDeclaration,WeakPtrExtension> m_vertexDeclaration;
		RefPtr<HardWareBuffer,WeakPtrExtension> m_vertexData;
		RenderablesMap m_renderables;
		Mutex m_walkMutex;


	};


	/**
	*   Factory for creating scene manager objects.
	*/
	class OctreeSceneManagerFactory: public SceneManagerFactory
	{
	public:

		OctreeSceneManagerFactory():m_type("Octree"){}

		/**
		*   Returns the factory type.
		*/
		virtual const std::string& GetType() const {return m_type;}

		/**
		*   Creates a new object.
		*   @param name Name of the object to create
		*   @return	An object created by the factory. The type of the object depends on	the factory.
		*/
		virtual OctreeSceneManager* CreateInstance( const std::string& name);

	private:

		std::string m_type;
	};

}

#endif
